package com.dwy2002.common.utils;


import com.dwy2002.common.exception.ValidationException;
import com.dwy2002.enums.Status;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

import static com.dwy2002.common.utils.Utils.isEmptyOrNull;

/**
 * 校验工具类（核心）
 *
 * @author 杜伟毅
 * @version 1.0
 * @since 2025/02/01
 */
public class Validator<T> {
    private final T target;
    private final List<String> errors = new ArrayList<>();

    private Validator(T target) {
        this.target = target;
    }

    /**
     * 创建校验器实例
     *
     * @param target 需要校验的目标对象
     * @param <T>    目标对象类型
     * @return 绑定目标对象的校验器实例
     * @author 杜伟毅
     * @since 2025/2/1 0001 16:23
     */
    public static <T> Validator<T> of(T target) {
        return new Validator<>(target);
    }

    /**
     * 校验字段非空（非null且非空字符串）
     *
     * @param getter   字段获取方法引用（如：User::getName）
     * @param errorMsg 校验失败时的错误提示
     * @return 当前校验器实例（支持链式调用）
     * @author 杜伟毅
     * @since 2025/2/1 0001 15:17
     */
    public Validator<T> notEmpty(Function<T, Object> getter, String errorMsg) {
        Object value = getter.apply(target);
        if (value == null || (value instanceof String && ((String) value).isEmpty())) {
            errors.add(errorMsg);
        }
        return this;
    }

    /**
     * 校验BigDecimal字段非空且大于零
     *
     * @param getter   字段获取方法引用（如：Order::getAmount）
     * @param errorMsg 校验失败时的错误提示
     * @return 当前校验器实例（支持链式调用）
     * @author 杜伟毅
     * @since 2025/2/1 0001 15:45
     */
    public Validator<T> notZeroDecimal(Function<T, BigDecimal> getter, String errorMsg) {
        BigDecimal value = getter.apply(target);
        if (value == null || value.compareTo(BigDecimal.ZERO) <= 0) {
            errors.add(errorMsg);
        }
        return this;
    }

    /**
     * 校验整数字段值大于指定最小值
     *
     * @param getter   字段获取方法引用（如：Product::getStock）
     * @param min      允许的最小值（不包含）
     * @param errorMsg 校验失败时的错误提示
     * @return 当前校验器实例（支持链式调用）
     * @author 杜伟毅
     * @since 2025/2/1 0001 14:17
     */
    public Validator<T> minValue(Function<T, Integer> getter, int min, String errorMsg) {
        Integer value = getter.apply(target);
        if (value == null || value < min) {
            errors.add(errorMsg);
        }
        return this;
    }

    /**
     * 校验整数字段值在指定范围内,并同步判空
     *
     * @param getter       字段获取方法引用（如：Course::getStatus）
     * @param maxExclusive 允许的最大值（不包含）
     * @param minExclusive 允许的最小值（不包含）
     * @param errorMsg     校验失败时的错误提示
     * @return 当前校验器实例（支持链式调用）
     * @author 杜伟毅
     * @since 2025/2/1 0001 15:17
     */
    public Validator<T> inRange(Function<T, Integer> getter, int maxExclusive, int minExclusive, String errorMsg) {
        Integer value = getter.apply(target);
        if (value == null || value >= maxExclusive && minExclusive >= value) {
            errors.add(errorMsg);
        }
        return this;
    }

    /**
     * 校验字段非null
     *
     * @param getter   字段获取方法引用（如：Order::getCreateTime）
     * @param errorMsg 校验失败时的错误提示
     * @return 当前校验器实例（支持链式调用）
     * @author 杜伟毅
     * @since 2025/2/1 0001 15:17
     */
    public Validator<T> notNull(Function<T, Object> getter, String errorMsg) {
        Object value = getter.apply(target);
        if (value == null) {
            errors.add(errorMsg);
        }
        return this;
    }

    /**
     * 添加校验规则：当条件不满足时记录错误信息
     *
     * @param conditionPassed 校验条件结果，true表示校验通过，false表示校验失败
     * @param errorMsg        校验失败时的错误提示
     * @return true：表示校验通过 false：表示校验失败（支持链式调用）
     * @author 杜伟毅
     * @since 2025/3/8 0008 0:08
     */
    public Validator<T> mustSatisfy(boolean conditionPassed, String errorMsg) {
        if (conditionPassed) {
            errors.add(errorMsg);
        }
        return this;
    }

    /**
     * 校验权限码格式规范
     * <p>
     * 校验规则：
     * 1. 非空且长度≥2字符
     * 2. 首字母必须为大写字母（A-Z）
     * 3. 第二位必须为非零数字（1-9）
     * 4. 后续字符（如有）必须为数字
     *
     * @param permCode 待校验的权限码
     * @return Validator实例（支持链式调用）
     * @author 杜伟毅
     * @since 2025/4/4 0004 1:41
     */
    public Validator<T> validatePermCodeFormat(String permCode) {
        // 前置空值校验（必须优先处理）
        if (isEmptyOrNull(permCode)) {
            errors.add("权限码不能为空");
            return this;
        }

        // 最小长度
        if (permCode.length() < 2) {
            errors.add("权限码长度不足");
            return this; // 提前返回避免后续空指针
        }

        // 首字母必须大写
        char firstChar = permCode.charAt(0);
        if (!Character.isUpperCase(firstChar)) { // 正确检测大小写
            errors.add("首字母必须是大写字母");
        }
        char secondChar = permCode.charAt(1);
        if (secondChar < '1' || secondChar > '9') {
            errors.add("第二位禁止使用0");
        }

        if (permCode.length() > 2) { // 检查后续是否有非法字符
            for (int i = 2; i < permCode.length(); i++) {
                if (!Character.isDigit(permCode.charAt(i))) {
                    errors.add("包含非法字符");
                }
            }
        }
        return this;
    }

    /**
     * 执行校验并抛出包含所有错误信息的异常
     *
     * @throws ValidationException 当存在校验错误时抛出
     * @author 杜伟毅
     * @since 2025/2/1 0001 15:17
     */
    public void validate() {
        if (!errors.isEmpty()) {
            throw new ValidationException(Status.BAD_REQUEST, errors);
        }
    }
}
